 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.forms.widgets;

 import java.util.Hashtable ;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.ToolBarManager;
 import org.eclipse.jface.dialogs.Dialog;
 import org.eclipse.jface.dialogs.IMessageProvider;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CLabel;
 import org.eclipse.swt.dnd.DragSourceListener;
 import org.eclipse.swt.dnd.DropTargetListener;
 import org.eclipse.swt.dnd.Transfer;
 import org.eclipse.swt.events.DisposeEvent;
 import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.MouseMoveListener;
 import org.eclipse.swt.events.MouseTrackListener;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.FontMetrics;
 import org.eclipse.swt.graphics.GC;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Canvas;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Layout;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.ToolBar;
 import org.eclipse.ui.forms.IFormColors;
 import org.eclipse.ui.forms.IMessage;
 import org.eclipse.ui.forms.events.IHyperlinkListener;
 import org.eclipse.ui.forms.widgets.Hyperlink;
 import org.eclipse.ui.forms.widgets.ILayoutExtension;
 import org.eclipse.ui.forms.widgets.SizeCache;
 import org.eclipse.ui.internal.forms.IMessageToolTipManager;
 import org.eclipse.ui.internal.forms.MessageManager;

 /**
  * Form header moved out of the form class.
  */
 public class FormHeading extends Canvas {
     private static final int TITLE_HMARGIN = 1;
     private static final int SPACING = 5;
     private static final int VSPACING = 5;
     private static final int HMARGIN = 6;
     private static final int VMARGIN = 1;
     private static final int CLIENT_MARGIN = 1;

     private static final int SEPARATOR = 1 << 1;
     private static final int BOTTOM_TOOLBAR = 1 << 2;
     private static final int BACKGROUND_IMAGE_TILED = 1 << 3;
     private static final int SEPARATOR_HEIGHT = 2;
     private static final int MESSAGE_AREA_LIMIT = 50;
     static IMessage[] NULL_MESSAGE_ARRAY = new IMessage[] {};

     public static final String COLOR_BASE_BG = "baseBg"; //$NON-NLS-1$

     private Image backgroundImage;

     private Image gradientImage;

     Hashtable colors = new Hashtable ();

     private int flags;

     private GradientInfo gradientInfo;

     private ToolBarManager toolBarManager;

     private SizeCache toolbarCache = new SizeCache();

     private SizeCache clientCache = new SizeCache();

     private SizeCache messageCache = new SizeCache();

     private TitleRegion titleRegion;

     private MessageRegion messageRegion;

     private IMessageToolTipManager messageToolTipManager = new DefaultMessageToolTipManager();

     private Control headClient;

     private class DefaultMessageToolTipManager implements
             IMessageToolTipManager {
         public void createToolTip(Control control, boolean imageLabel) {
         }

         public void update() {
             String details = getMessageType() == 0 ? null : MessageManager
                     .createDetails(getChildrenMessages());
             if (messageRegion != null)
                 messageRegion.updateToolTip(details);
             if (getMessageType() > 0
                     && (details == null || details.length() == 0))
                 details = getMessage();
             titleRegion.updateToolTip(details);
         }
     }

     private class GradientInfo {
         Color[] gradientColors;

         int[] percents;

         boolean vertical;
     }

     private class FormHeadingLayout extends Layout implements ILayoutExtension {
         public int computeMinimumWidth(Composite composite, boolean flushCache) {
             return computeSize(composite, 5, SWT.DEFAULT, flushCache).x;
         }

         public int computeMaximumWidth(Composite composite, boolean flushCache) {
             return computeSize(composite, SWT.DEFAULT, SWT.DEFAULT, flushCache).x;
         }

         public Point computeSize(Composite composite, int wHint, int hHint,
                 boolean flushCache) {
             return layout(composite, false, 0, 0, wHint, hHint, flushCache);
         }

         protected void layout(Composite composite, boolean flushCache) {
             Rectangle rect = composite.getClientArea();
             layout(composite, true, rect.x, rect.y, rect.width, rect.height,
                     flushCache);
         }

         private Point layout(Composite composite, boolean move, int x, int y,
                 int width, int height, boolean flushCache) {
             Point tsize = null;
             Point msize = null;
             Point tbsize = null;
             Point clsize = null;

             if (flushCache) {
                 clientCache.flush();
                 messageCache.flush();
                 toolbarCache.flush();
             }
             if (hasToolBar()) {
                 ToolBar tb = toolBarManager.getControl();
                 toolbarCache.setControl(tb);
                 tbsize = toolbarCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
             }
             if (headClient != null) {
                 clientCache.setControl(headClient);
                 int cwhint = width;
                 if (cwhint != SWT.DEFAULT) {
                     cwhint -= HMARGIN * 2;
                     if (tbsize != null && getToolBarAlignment() == SWT.BOTTOM)
                         cwhint -= tbsize.x + SPACING;
                 }
                 clsize = clientCache.computeSize(cwhint, SWT.DEFAULT);
             }
             int totalFlexWidth = width;
             int flexWidth = totalFlexWidth;
             if (totalFlexWidth != SWT.DEFAULT) {
                 totalFlexWidth -= TITLE_HMARGIN * 2;
                 // complete right margin
 if (hasToolBar() && getToolBarAlignment() == SWT.TOP
                         || hasMessageRegion())
                     totalFlexWidth -= SPACING;
                 // subtract tool bar
 if (hasToolBar() && getToolBarAlignment() == SWT.TOP)
                     totalFlexWidth -= tbsize.x + SPACING;
                 flexWidth = totalFlexWidth;
                 if (hasMessageRegion()) {
                     // remove message region spacing and divide by 2
 flexWidth -= SPACING;
                     // flexWidth /= 2;
 }
             }
             /*
              * // compute text and message sizes tsize =
              * titleRegion.computeSize(flexWidth, SWT.DEFAULT); if (flexWidth !=
              * SWT.DEFAULT && tsize.x < flexWidth) flexWidth += flexWidth -
              * tsize.x;
              *
              * if (hasMessageRegion()) {
              * messageCache.setControl(messageRegion.getMessageControl()); msize =
              * messageCache.computeSize(flexWidth, SWT.DEFAULT); int maxWidth =
              * messageCache.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; if
              * (maxWidth < msize.x) { msize.x = maxWidth; // recompute title
              * with the reclaimed width int tflexWidth = totalFlexWidth -
              * SPACING - msize.x; tsize = titleRegion.computeSize(tflexWidth,
              * SWT.DEFAULT); } }
              */
             if (!hasMessageRegion()) {
                 tsize = titleRegion.computeSize(flexWidth, SWT.DEFAULT);
             } else {
                 // Total flexible area in the first row is flexWidth.
 // Try natural widths of title and
 Point tsizeNatural = titleRegion.computeSize(SWT.DEFAULT,
                         SWT.DEFAULT);
                 messageCache.setControl(messageRegion.getMessageControl());
                 Point msizeNatural = messageCache.computeSize(SWT.DEFAULT,
                         SWT.DEFAULT);
                 // try to fit all
 tsize = tsizeNatural;
                 msize = msizeNatural;
                 if (flexWidth != SWT.DEFAULT) {
                     int needed = tsizeNatural.x + msizeNatural.x;
                     if (needed > flexWidth) {
                         // too big - try to limit the message
 int mwidth = flexWidth - tsizeNatural.x;
                         if (mwidth >= MESSAGE_AREA_LIMIT) {
                             msize.x = mwidth;
                         } else {
                             // message is squeezed to the limit
 int flex = flexWidth - MESSAGE_AREA_LIMIT;
                             tsize = titleRegion.computeSize(flex, SWT.DEFAULT);
                             msize.x = MESSAGE_AREA_LIMIT;
                         }
                     }
                 }
             }

             Point size = new Point(width, height);
             if (!move) {
                 // compute sizes
 int width1 = 2 * TITLE_HMARGIN;
                 width1 += tsize.x;
                 if (msize != null)
                     width1 += SPACING + msize.x;
                 if (tbsize != null && getToolBarAlignment() == SWT.TOP)
                     width1 += SPACING + tbsize.x;
                 if (msize != null
                         || (tbsize != null && getToolBarAlignment() == SWT.TOP))
                     width1 += SPACING;
                 size.x = width1;
                 if (clsize != null) {
                     int width2 = clsize.x;
                     if (tbsize != null && getToolBarAlignment() == SWT.BOTTOM)
                         width2 += SPACING + tbsize.x;
                     width2 += 2 * HMARGIN;
                     size.x = Math.max(width1, width2);
                 }
                 // height, first row
 size.y = tsize.y;
                 if (msize != null)
                     size.y = Math.max(msize.y, size.y);
                 if (tbsize != null && getToolBarAlignment() == SWT.TOP)
                     size.y = Math.max(tbsize.y, size.y);
                 if (size.y > 0)
                     size.y += VMARGIN * 2;
                 // add second row
 int height2 = 0;
                 if (tbsize != null && getToolBarAlignment() == SWT.BOTTOM)
                     height2 = tbsize.y;
                 if (clsize != null)
                     height2 = Math.max(height2, clsize.y);
                 if (height2 > 0)
                     size.y += VSPACING + height2 + CLIENT_MARGIN;
                 // add separator
 if (size.y > 0 && isSeparatorVisible())
                     size.y += SEPARATOR_HEIGHT;
             } else {
                 // position controls
 int xloc = x;
                 int yloc = y + VMARGIN;
                 int row1Height = tsize.y;
                 if (hasMessageRegion())
                     row1Height = Math.max(row1Height, msize.y);
                 if (hasToolBar() && getToolBarAlignment() == SWT.TOP)
                     row1Height = Math.max(row1Height, tbsize.y);
                 titleRegion.setBounds(xloc,
                 // yloc + row1Height / 2 - tsize.y / 2,
 yloc, tsize.x, tsize.y);
                 xloc += tsize.x;

                 if (hasMessageRegion()) {
                     xloc += SPACING;
                     int messageOffset = 0;
                     if (tsize.y > 0) {
                         // space between title area and title text
 int titleLeadingSpace = (tsize.y - titleRegion.getFontHeight()) / 2;
                         // space between message control and message text
 int messageLeadingSpace = (msize.y - messageRegion.getFontHeight()) / 2;
                         // how much to offset the message so baselines align
 messageOffset = (titleLeadingSpace + titleRegion.getFontBaselineHeight())
                             - (messageLeadingSpace + messageRegion.getFontBaselineHeight());
                     }

                     messageRegion
                             .getMessageControl()
                             .setBounds(
                                     xloc,
                                     tsize.y > 0 ? (yloc + messageOffset)
                                             : (yloc + row1Height / 2 - msize.y / 2),
                                     msize.x, msize.y);
                     xloc += msize.x;
                 }
                 if (toolBarManager != null)
                     toolBarManager.getControl().setVisible(
                             !toolBarManager.isEmpty());
                 if (tbsize != null && getToolBarAlignment() == SWT.TOP) {
                     ToolBar tbar = toolBarManager.getControl();
                     tbar.setBounds(x + width - 1 - tbsize.x - HMARGIN, yloc
                             + row1Height - 1 - tbsize.y, tbsize.x, tbsize.y);
                 }
                 // second row
 xloc = HMARGIN;
                 yloc += row1Height + VSPACING;
                 int tw = 0;

                 if (tbsize != null && getToolBarAlignment() == SWT.BOTTOM) {
                     ToolBar tbar = toolBarManager.getControl();
                     tbar.setBounds(x + width - 1 - tbsize.x - HMARGIN, yloc,
                             tbsize.x, tbsize.y);
                     tw = tbsize.x + SPACING;
                 }
                 if (headClient != null) {
                     int carea = width - HMARGIN * 2 - tw;
                     headClient.setBounds(xloc, yloc, carea, clsize.y);
                 }
             }
             return size;
         }
     }

     private boolean hasToolBar() {
         return toolBarManager != null && !toolBarManager.isEmpty();
     }

     private boolean hasMessageRegion() {
         return messageRegion != null && !messageRegion.isEmpty();
     }

     private class MessageRegion {
         private String message;
         private int messageType;
         private CLabel messageLabel;
         private IMessage[] messages;
         private Hyperlink messageHyperlink;
         private ListenerList listeners;
         private Color fg;
         private int fontHeight = -1;
         private int fontBaselineHeight = -1;

         public MessageRegion() {
         }

         public boolean isDisposed() {
             Control c = getMessageControl();
             return c != null && c.isDisposed();
         }

         public boolean isEmpty() {
             Control c = getMessageControl();
             if (c == null)
                 return true;
             return !c.getVisible();
         }

         public int getFontHeight() {
             if (fontHeight == -1) {
                 Control c = getMessageControl();
                 if (c == null)
                     return 0;
                 GC gc = new GC(c.getDisplay());
                 gc.setFont(c.getFont());
                 fontHeight = gc.getFontMetrics().getHeight();
                 gc.dispose();
             }
             return fontHeight;
         }

         public int getFontBaselineHeight() {
             if (fontBaselineHeight == -1) {
                 Control c = getMessageControl();
                 if (c == null)
                     return 0;
                 GC gc = new GC(c.getDisplay());
                 gc.setFont(c.getFont());
                 FontMetrics fm = gc.getFontMetrics();
                 fontBaselineHeight = fm.getHeight() - fm.getDescent();
                 gc.dispose();
             }
             return fontBaselineHeight;
         }

         public void showMessage(String newMessage, int newType,
                 IMessage[] messages) {
             Control oldControl = getMessageControl();
             int oldType = messageType;
             this.message = newMessage;
             this.messageType = newType;
             this.messages = messages;
             if (newMessage == null) {
                 // clearing of the message
 if (oldControl != null && oldControl.getVisible())
                     oldControl.setVisible(false);
                 return;
             }
             ensureControlExists();
             if (needHyperlink()) {
                 messageHyperlink.setText(newMessage);
                 messageHyperlink.setHref(messages);
             } else {
                 messageLabel.setText(newMessage);
             }
             if (oldType != newType)
                 updateForeground();
         }

         public void updateToolTip(String toolTip) {
             Control control = getMessageControl();
             if (control != null)
                 control.setToolTipText(toolTip);
         }

         public String getMessage() {
             return message;
         }

         public int getMessageType() {
             return messageType;
         }

         public IMessage[] getChildrenMessages() {
             return messages;
         }

         public String getDetailedMessage() {
             Control c = getMessageControl();
             if (c != null)
                 return c.getToolTipText();
             return null;
         }

         public Control getMessageControl() {
             if (needHyperlink() && messageHyperlink != null)
                 return messageHyperlink;
             return messageLabel;
         }

         public Image getMessageImage() {
             switch (messageType) {
             case IMessageProvider.INFORMATION:
                 return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_INFO);
             case IMessageProvider.WARNING:
                 return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING);
             case IMessageProvider.ERROR:
                 return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR);
             default:
                 return null;
             }
         }

         public void addMessageHyperlinkListener(IHyperlinkListener listener) {
             if (listeners == null)
                 listeners = new ListenerList();
             listeners.add(listener);
             ensureControlExists();
             if (messageHyperlink != null)
                 messageHyperlink.addHyperlinkListener(listener);
             if (listeners.size() == 1)
                 updateForeground();
         }

         private void removeMessageHyperlinkListener(IHyperlinkListener listener) {
             if (listeners != null) {
                 listeners.remove(listener);
                 if (messageHyperlink != null)
                     messageHyperlink.removeHyperlinkListener(listener);
                 if (listeners.isEmpty())
                     listeners = null;
                 ensureControlExists();
                 if (listeners == null && !isDisposed())
                     updateForeground();
             }
         }

         private void ensureControlExists() {
             if (needHyperlink()) {
                 if (messageLabel != null)
                     messageLabel.setVisible(false);
                 if (messageHyperlink == null) {
                     messageHyperlink = new Hyperlink(FormHeading.this, SWT.NULL);
                     messageHyperlink.setUnderlined(true);
                     messageHyperlink.setText(message);
                     messageHyperlink.setHref(messages);
                     Object [] llist = listeners.getListeners();
                     for (int i = 0; i < llist.length; i++)
                         messageHyperlink
                                 .addHyperlinkListener((IHyperlinkListener) llist[i]);
                     if (messageToolTipManager != null)
                         messageToolTipManager.createToolTip(messageHyperlink, false);
                 } else if (!messageHyperlink.getVisible()) {
                     messageHyperlink.setText(message);
                     messageHyperlink.setHref(messages);
                     messageHyperlink.setVisible(true);
                 }
             } else {
                 // need a label
 if (messageHyperlink != null)
                     messageHyperlink.setVisible(false);
                 if (messageLabel == null) {
                     messageLabel = new CLabel(FormHeading.this, SWT.NULL);
                     messageLabel.setText(message);
                     if (messageToolTipManager != null)
                         messageToolTipManager.createToolTip(messageLabel, false);
                 } else if (!messageLabel.getVisible()) {
                     messageLabel.setText(message);
                     messageLabel.setVisible(true);
                 }
             }
             layout(true);
         }

         private boolean needHyperlink() {
             return messageType > 0 && listeners != null;
         }

         public void setBackground(Color bg) {
             if (messageHyperlink != null)
                 messageHyperlink.setBackground(bg);
             if (messageLabel != null)
                 messageLabel.setBackground(bg);
         }

         public void setForeground(Color fg) {
             this.fg = fg;
         }

         private void updateForeground() {
             Color theFg;

             switch (messageType) {
             case IMessageProvider.ERROR:
                 theFg = getDisplay().getSystemColor(SWT.COLOR_RED);
                 break;
             case IMessageProvider.WARNING:
                 theFg = getDisplay().getSystemColor(SWT.COLOR_DARK_YELLOW);
                 break;
             default:
                 theFg = fg;
             }
             getMessageControl().setForeground(theFg);
         }
     }

     /**
      * Creates the form content control as a child of the provided parent.
      *
      * @param parent
      * the parent widget
      */
     public FormHeading(Composite parent, int style) {
         super(parent, style);
         setBackgroundMode(SWT.INHERIT_DEFAULT);
         addListener(SWT.Paint, new Listener() {
             public void handleEvent(Event e) {
                 onPaint(e.gc);
             }
         });
         addListener(SWT.Dispose, new Listener() {
             public void handleEvent(Event e) {
                 if (gradientImage != null) {
                     gradientImage.dispose();
                     gradientImage = null;
                 }
             }
         });
         addListener(SWT.Resize, new Listener() {
             public void handleEvent(Event e) {
                 if (gradientInfo != null
                         || (backgroundImage != null && !isBackgroundImageTiled()))
                     updateGradientImage();
             }
         });
         addMouseMoveListener(new MouseMoveListener() {
             public void mouseMove(MouseEvent e) {
                 updateTitleRegionHoverState(e);
             }
         });
         addMouseTrackListener(new MouseTrackListener() {
             public void mouseEnter(MouseEvent e) {
                 updateTitleRegionHoverState(e);
             }

             public void mouseExit(MouseEvent e) {
                 titleRegion.setHoverState(TitleRegion.STATE_NORMAL);
             }

             public void mouseHover(MouseEvent e) {
             }
         });
         super.setLayout(new FormHeadingLayout());
         titleRegion = new TitleRegion(this);
     }

     /**
      * Fully delegates the size computation to the internal layout manager.
      */
     public final Point computeSize(int wHint, int hHint, boolean changed) {
         return ((FormHeadingLayout) getLayout()).computeSize(this, wHint,
                 hHint, changed);
     }

     /**
      * Prevents from changing the custom control layout.
      */
     public final void setLayout(Layout layout) {
     }

     /**
      * Returns the title text that will be rendered at the top of the form.
      *
      * @return the title text
      */
     public String getText() {
         return titleRegion.getText();
     }

     /**
      * Returns the title image that will be rendered to the left of the title.
      *
      * @return the title image
      * @since 3.2
      */
     public Image getImage() {
         return titleRegion.getImage();
     }

     /**
      * Sets the background color of the header.
      */
     public void setBackground(Color bg) {
         super.setBackground(bg);
         internalSetBackground(bg);
     }

     private void internalSetBackground(Color bg) {
         titleRegion.setBackground(bg);
         if (messageRegion != null)
             messageRegion.setBackground(bg);
         if (toolBarManager != null)
             toolBarManager.getControl().setBackground(bg);
         putColor(COLOR_BASE_BG, bg);
     }

     /**
      * Sets the foreground color of the header.
      */
     public void setForeground(Color fg) {
         super.setForeground(fg);
         titleRegion.setForeground(fg);
         if (messageRegion != null)
             messageRegion.setForeground(fg);
     }

     /**
      * Sets the text to be rendered at the top of the form above the body as a
      * title.
      *
      * @param text
      * the title text
      */
     public void setText(String text) {
         titleRegion.setText(text);
     }

     public void setFont(Font font) {
         super.setFont(font);
         titleRegion.setFont(font);
     }

     /**
      * Sets the image to be rendered to the left of the title.
      *
      * @param image
      * the title image or <code>null</code> to show no image.
      * @since 3.2
      */
     public void setImage(Image image) {
         titleRegion.setImage(image);
         if (messageRegion != null)
             titleRegion.updateImage(messageRegion.getMessageImage(), true);
         else
             titleRegion.updateImage(null, true);
     }

     public void setTextBackground(Color[] gradientColors, int[] percents,
             boolean vertical) {
         if (gradientColors != null) {
             gradientInfo = new GradientInfo();
             gradientInfo.gradientColors = gradientColors;
             gradientInfo.percents = percents;
             gradientInfo.vertical = vertical;
             setBackground(null);
             updateGradientImage();
         } else {
             // reset
 gradientInfo = null;
             if (gradientImage != null) {
                 gradientImage.dispose();
                 gradientImage = null;
                 setBackgroundImage(null);
             }
         }
     }

     public void setHeadingBackgroundImage(Image image) {
         this.backgroundImage = image;
         if (image != null)
             setBackground(null);
         if (isBackgroundImageTiled()) {
             setBackgroundImage(image);
         } else
             updateGradientImage();
     }

     public Image getHeadingBackgroundImage() {
         return backgroundImage;
     }

     public void setBackgroundImageTiled(boolean tiled) {
         if (tiled)
             flags |= BACKGROUND_IMAGE_TILED;
         else
             flags &= ~BACKGROUND_IMAGE_TILED;
         setHeadingBackgroundImage(this.backgroundImage);
     }

     public boolean isBackgroundImageTiled() {
         return (flags & BACKGROUND_IMAGE_TILED) != 0;
     }

     public void setBackgroundImage(Image image) {
         super.setBackgroundImage(image);
         if (image != null) {
             internalSetBackground(null);
         }
     }

     /**
      * Returns the tool bar manager that is used to manage tool items in the
      * form's title area.
      *
      * @return form tool bar manager
      */
     public IToolBarManager getToolBarManager() {
         if (toolBarManager == null) {
             toolBarManager = new ToolBarManager(SWT.FLAT);
             ToolBar toolbar = toolBarManager.createControl(this);
             toolbar.setBackground(getBackground());
             toolbar.setForeground(getForeground());
             toolbar.setCursor(FormsResources.getHandCursor());
             addDisposeListener(new DisposeListener() {
                 public void widgetDisposed(DisposeEvent e) {
                     if (toolBarManager != null) {
                         toolBarManager.dispose();
                         toolBarManager = null;
                     }
                 }
             });
         }
         return toolBarManager;
     }

     /**
      * Returns the menu manager that is used to manage tool items in the form's
      * title area.
      *
      * @return form drop-down menu manager
      * @since 3.3
      */
     public IMenuManager getMenuManager() {
         return titleRegion.getMenuManager();
     }

     /**
      * Updates the local tool bar manager if used. Does nothing if local tool
      * bar manager has not been created yet.
      */
     public void updateToolBar() {
         if (toolBarManager != null)
             toolBarManager.update(false);
     }

     private void onPaint(GC gc) {
         if (!isSeparatorVisible() && getBackgroundImage() == null)
             return;
         Rectangle carea = getClientArea();
         Image buffer = new Image(getDisplay(), carea.width, carea.height);
         buffer.setBackground(getBackground());
         GC igc = new GC(buffer);
         igc.setBackground(getBackground());
         igc.fillRectangle(0, 0, carea.width, carea.height);
         if (getBackgroundImage() != null) {
             if (gradientInfo != null)
                 drawBackground(igc, carea.x, carea.y, carea.width, carea.height);
             else {
                 Image bgImage = getBackgroundImage();
                 Rectangle ibounds = bgImage.getBounds();
                 drawBackground(igc, carea.x, carea.y, ibounds.width,
                         ibounds.height);
             }
         }

         if (isSeparatorVisible()) {
             // bg separator
 if (hasColor(IFormColors.H_BOTTOM_KEYLINE1))
                 igc.setForeground(getColor(IFormColors.H_BOTTOM_KEYLINE1));
             else
                 igc.setForeground(getBackground());
             igc.drawLine(carea.x, carea.height - 2, carea.x + carea.width - 1,
                     carea.height - 2);
             if (hasColor(IFormColors.H_BOTTOM_KEYLINE2))
                 igc.setForeground(getColor(IFormColors.H_BOTTOM_KEYLINE2));
             else
                 igc.setForeground(getForeground());
             igc.drawLine(carea.x, carea.height - 1, carea.x + carea.width - 1,
                     carea.height - 1);
         }
         igc.dispose();
         gc.drawImage(buffer, carea.x, carea.y);
         buffer.dispose();
     }

     private void updateTitleRegionHoverState(MouseEvent e) {
         Rectangle titleRect = titleRegion.getBounds();
         titleRect.width += titleRect.x + 15;
         titleRect.height += titleRect.y + 15;
         titleRect.x = 0;
         titleRect.y = 0;
         if (titleRect.contains(e.x, e.y))
             titleRegion.setHoverState(TitleRegion.STATE_HOVER_LIGHT);
         else
             titleRegion.setHoverState(TitleRegion.STATE_NORMAL);
     }

     private void updateGradientImage() {
         Rectangle rect = getBounds();
         if (gradientImage != null) {
             gradientImage.dispose();
             gradientImage = null;
         }
         if (gradientInfo != null) {
             boolean vertical = gradientInfo.vertical;
             int width = vertical ? 1 : rect.width;
             int height = vertical ? rect.height : 1;
             gradientImage = new Image(getDisplay(), Math.max(width, 1), Math
                     .max(height, 1));
             GC gc = new GC(gradientImage);
             drawTextGradient(gc, width, height);
             gc.dispose();
         } else if (backgroundImage != null && !isBackgroundImageTiled()) {
             gradientImage = new Image(getDisplay(), Math.max(rect.width, 1),
                     Math.max(rect.height, 1));
             gradientImage.setBackground(getBackground());
             GC gc = new GC(gradientImage);
             gc.drawImage(backgroundImage, 0, 0);
             gc.dispose();
         }
         setBackgroundImage(gradientImage);
     }

     private void drawTextGradient(GC gc, int width, int height) {
         final Color oldBackground = gc.getBackground();
         if (gradientInfo.gradientColors.length == 1) {
             if (gradientInfo.gradientColors[0] != null)
                 gc.setBackground(gradientInfo.gradientColors[0]);
             gc.fillRectangle(0, 0, width, height);
         } else {
             final Color oldForeground = gc.getForeground();
             Color lastColor = gradientInfo.gradientColors[0];
             if (lastColor == null)
                 lastColor = oldBackground;
             int pos = 0;
             for (int i = 0; i < gradientInfo.percents.length; ++i) {
                 gc.setForeground(lastColor);
                 lastColor = gradientInfo.gradientColors[i + 1];
                 if (lastColor == null)
                     lastColor = oldBackground;
                 gc.setBackground(lastColor);
                 if (gradientInfo.vertical) {
                     final int gradientHeight = (gradientInfo.percents[i]
                             * height / 100)
                             - pos;
                     gc.fillGradientRectangle(0, pos, width, gradientHeight,
                             true);
                     pos += gradientHeight;
                 } else {
                     final int gradientWidth = (gradientInfo.percents[i] * width / 100)
                             - pos;
                     gc.fillGradientRectangle(pos, 0, gradientWidth, height,
                             false);
                     pos += gradientWidth;
                 }
             }
             if (gradientInfo.vertical && pos < height) {
                 gc.setBackground(getColor(COLOR_BASE_BG));
                 gc.fillRectangle(0, pos, width, height - pos);
             }
             if (!gradientInfo.vertical && pos < width) {
                 gc.setBackground(getColor(COLOR_BASE_BG));
                 gc.fillRectangle(pos, 0, width - pos, height);
             }
             gc.setForeground(oldForeground);
         }
     }

     public boolean isSeparatorVisible() {
         return (flags & SEPARATOR) != 0;
     }

     public void setSeparatorVisible(boolean addSeparator) {
         if (addSeparator)
             flags |= SEPARATOR;
         else
             flags &= ~SEPARATOR;
     }

     public void setToolBarAlignment(int alignment) {
         if (alignment == SWT.BOTTOM)
             flags |= BOTTOM_TOOLBAR;
         else
             flags &= ~BOTTOM_TOOLBAR;
     }

     public int getToolBarAlignment() {
         return (flags & BOTTOM_TOOLBAR) != 0 ? SWT.BOTTOM : SWT.TOP;
     }

     public void addMessageHyperlinkListener(IHyperlinkListener listener) {
         ensureMessageRegionExists();
         messageRegion.addMessageHyperlinkListener(listener);
     }

     public void removeMessageHyperlinkListener(IHyperlinkListener listener) {
         if (messageRegion != null)
             messageRegion.removeMessageHyperlinkListener(listener);
     }

     public String getMessage() {
         return messageRegion != null ? messageRegion.getMessage() : null;
     }

     public int getMessageType() {
         return messageRegion != null ? messageRegion.getMessageType() : 0;
     }

     public IMessage[] getChildrenMessages() {
         return messageRegion != null ? messageRegion.getChildrenMessages()
                 : NULL_MESSAGE_ARRAY;
     }

     private void ensureMessageRegionExists() {
         // ensure message region exists
 if (messageRegion == null)
             messageRegion = new MessageRegion();
     }

     public void showMessage(String newMessage, int type, IMessage[] messages) {
         if (messageRegion == null) {
             // check the trivial case
 if (newMessage == null)
                 return;
         } else if (messageRegion.isDisposed())
             return;
         ensureMessageRegionExists();
         messageRegion.showMessage(newMessage, type, messages);
         titleRegion.updateImage(messageRegion.getMessageImage(), false);
         if (messageToolTipManager != null)
             messageToolTipManager.update();
         layout();
         redraw();
     }

     /**
      * Tests if the form is in the 'busy' state.
      *
      * @return <code>true</code> if busy, <code>false</code> otherwise.
      */

     public boolean isBusy() {
         return titleRegion.isBusy();
     }

    /**
     * Sets the form's busy state. Busy form will display 'busy' animation in
     * the area of the title image.
     *
     * @param busy
     * the form's busy state
     */

    public void setBusy(boolean busy) {
        if (titleRegion.setBusy(busy))
            layout();
    }

    public Control getHeadClient() {
        return headClient;
    }

    public void setHeadClient(Control headClient) {
        if (headClient != null)
            Assert.isTrue(headClient.getParent() == this);
        this.headClient = headClient;
        layout();
    }

    public void putColor(String key, Color color) {
        if (color == null)
            colors.remove(key);
        else
            colors.put(key, color);
    }

    public Color getColor(String key) {
        return (Color) colors.get(key);
    }

    public boolean hasColor(String key) {
        return colors.containsKey(key);
    }

    public void addDragSupport(int operations, Transfer[] transferTypes,
            DragSourceListener listener) {
        titleRegion.addDragSupport(operations, transferTypes, listener);
    }

    public void addDropSupport(int operations, Transfer[] transferTypes,
            DropTargetListener listener) {
        titleRegion.addDropSupport(operations, transferTypes, listener);
    }

    public IMessageToolTipManager getMessageToolTipManager() {
        return messageToolTipManager;
    }

    public void setMessageToolTipManager(
            IMessageToolTipManager messageToolTipManager) {
        this.messageToolTipManager = messageToolTipManager;
    }
}

